﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Version = Lucene.Net.Util.Version;

namespace PsiIndexer
{
  public class LuceneIndexer
  {
    private readonly Analyzer analyzer;
    private readonly Directory index;
    private readonly IndexWriter indexWriter;
    private QueryParser queryParser;
    private IndexSearcher searcher;

    public LuceneIndexer()
    {
      //create an analyzer to process the text
      this.analyzer = new StandardAnalyzer(Version.LUCENE_29);
      this.index = new RAMDirectory();
      this.indexWriter = new IndexWriter(this.index, this.analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
    }

    public void Index(IEnumerable<PsiConversation> conversations)
    {
      Parallel.ForEach(conversations.SelectMany(pc => pc.Chats), psi => addChat(this.indexWriter, psi));
    }

    public void OptimizeAndClose()
    {
      this.indexWriter.Optimize();
      this.indexWriter.Close();
    }


    private static void addChat(IndexWriter w, PsiChatInfo psi)
    {
      Document doc = new Document();
      doc.Add(new Field(LuceneMagicStrings.ID, psi.ID.ToString(), Field.Store.YES, Field.Index.ANALYZED)); // do i need this analysed?
      doc.Add(new Field(LuceneMagicStrings.Text, psi.Text, Field.Store.YES, Field.Index.ANALYZED));
      doc.Add(new Field(LuceneMagicStrings.Author, psi.Conversation.Author, Field.Store.YES, Field.Index.ANALYZED));
      // just save this
      doc.Add(new Field(LuceneMagicStrings.File, psi.Conversation.OriginFile, Field.Store.YES, Field.Index.NOT_ANALYZED));
      doc.Add(new Field(LuceneMagicStrings.Time, psi.Time.ToLongTimeString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
      doc.Add(new Field(LuceneMagicStrings.Direction, psi.Direction.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
      w.AddDocument(doc);
    }

    public SearchResult Search(string toSearch4, int maxResult)
    {
      SearchResult ret;
      try {
        if (this.searcher == null) {
          //create an index searcher that will perform the search
          this.searcher = new IndexSearcher(this.index, true);

          //create the query parser, with the default search feild set to "content"
          this.queryParser = new QueryParser(Version.LUCENE_29, LuceneMagicStrings.Text, this.analyzer);
        }

        Stopwatch sw = Stopwatch.StartNew();
        //parse the query string into a Query object
        Query query = this.queryParser.Parse(toSearch4);
        TopScoreDocCollector collector = TopScoreDocCollector.create(maxResult, true);
        this.searcher.Search(query, collector);
        ScoreDoc[] hits = collector.TopDocs().scoreDocs;
        IEnumerable<Document> docHits = hits.Select(sd => this.searcher.Doc(sd.doc));
        sw.Stop();
        ret = new SearchResult {Hits = docHits, Searcher = this.searcher, DurationInMS = sw.ElapsedMilliseconds, Success = true};
      }
      catch (Exception ex) {
        ret = new SearchResult {Success = false, Error = ex.Message};
      }
      return ret;
    }


  }

  public class SearchResult
  {
    public IEnumerable<Document> Hits { get; set; }
    public IndexSearcher Searcher { get; set; }
    public long DurationInMS { get; set; }
    public bool Success { get; set; }
    public string Error { get; set; }
  }
}